[Talend] エラーとなる可能性のある処理をリトライする
特定の処理でエラー(例外)が発生した際にリトライしたいケースありますよね?数秒ほど何らかの理由でデータベースへアクセスができない状態となった時など、数秒間置いてリトライする処理が組まれていれば全体の処理を止めずに完了することができます。そのような処理を Talend で作ってみました。
概要
今回作成したジョブの完成形は下図の通りです。
「リトライ」と銘打たれた(線で見難いですが……)コンポーネントから反復(Iterate)線で繋がれた処理が「エラーとなる可能性のある処理(下図の赤枠内)」となります。
今回は Redshift へアクセスし、「SELECT 'Hello, world!' AS message;」というクエリの結果を tLogRow でコンソールへ表示するだけの処理です。
エラー(例外)が発生した場合は、指定された試行回数・試行間隔でリトライを行い、成功した場合には「成功」ダイアログを、リトライに失敗した場合には「失敗」ダイアログを表示するように組み立てました。
試行回数・試行間隔の設定
試行回数(tryTimes)および試行間隔(tryInterval)は「コンテキスト」に設定しました。「コンテキスト」はそのジョブ全体で使われる値を設定しておける場所です。
- 「追加ボタン」を押下し、
- 追加された行の「Name」に名称を付け、
- データの型を指定し、
- 「Value」に実値を入力します。
ここでは試行回数(tryTimes)を 3 回、試行間隔(tryInterval)を 10 秒と設定しました。
リトライ処理の作成(tJavaFlex)
リトライ処理の実現にはコンポーネント「tJavaFlex」を使用します。
このコンポーネントは Java コードが書ける「tJava」系のコンポーネントですが、コードを書くエリアが「開始コード」「メインコード」「終了コード」に分かれている所が変わっている点です。
ここに下図のように Java コードを書きます。
int count = 0; while (true) { count++; try {
System.out.println("Try count: " + count);
break; } catch (Exception e) { if (context.tryTimes <= count) { throw e; } TimeUnit.SECONDS.sleep(context.tryInterval); } }
繋げると
int count = 0; while (true) { count++; try { // メインコード ここから System.out.println("Try count: " + count); // ★★★ エラーとなる可能性のある処理 ★★★ // メインコード ここまで break; } catch (Exception e) { if (context.tryTimes <= count) { throw e; } TimeUnit.SECONDS.sleep(context.tryInterval); } }
となります。
TimeUnit を使っているので、詳細設定のインポート欄に import 文を記述します。
処理としては、無限ループ内の try-catch 文で例外が発生した場合、試行回数を満たさない間は試行間隔(秒数)スリープ後にリトライ、試行回数を満たしたらキャッチした例外をそのまま throw する処理となっています。
メインコードに書かれたコードが無限ループの対象となりますが、tJavaFlex コンポーネントから「反復(Iterate)」線で繋がれた処理も対象となります。前述コードの「/* ★★★ エラーとなる可能性のある処理 ★★★ */」に処理が入るイメージですね。
tMsgBox コンポーネント(おまけ)
今回は成功・失敗の通知を tMsgBox を使って行うようにしました。
設定値はそれぞれ下図のような感じです。
実行
まずは普通に実行してみます。
成功しました!
次にネットワーク接続を切って実行してみます。
ジョブ Retry を 17:40 26/08/2016 に開始しました。 [statistics] connecting to socket on port 4069 [statistics] connected Try count: 1 Try count: 2 Try count: 3 Exception in component tRedshiftConnection_1 java.sql.SQLException: [Amazon](500150) Error setting/closing connection: UnknownHostException. at com.amazon.redshift.client.PGClient.connect(PGClient.java:537) at com.amazon.redshift.client.PGClient.<init>(PGClient.java:329) at com.amazon.redshift.core.PGJDBCConnection.connect(PGJDBCConnection.java:534) at com.amazon.jdbc.common.BaseConnectionFactory.doConnect(Unknown Source) at com.amazon.jdbc.common.AbstractDriver.connect(Unknown Source) at java.sql.DriverManager.getConnection(DriverManager.java:664) at java.sql.DriverManager.getConnection(DriverManager.java:247) at sample_project.retry_0_1.Retry.tJavaFlex_1Process(Retry.java:488) at sample_project.retry_0_1.Retry.runJobInTOS(Retry.java:1431) Caused by: com.amazon.support.exceptions.GeneralException: [Amazon](500150) Error setting/closing connection: UnknownHostException. ... 9 more Caused by: java.net.UnknownHostException at sun.nio.ch.Net.translateException(Net.java:155) at sun.nio.ch.SocketAdaptor.connect(SocketAdaptor.java:127) at com.amazon.redshift.client.PGClient.connect(PGClient.java:504) at com.amazon.redshift.client.PGClient.<init>(PGClient.java:329) at com.amazon.redshift.core.PGJDBCConnection.connect(PGJDBCConnection.java:534) at com.amazon.jdbc.common.BaseConnectionFactory.doConnect(Unknown Source) at com.amazon.jdbc.common.AbstractDriver.connect(Unknown Source) at java.sql.DriverManager.getConnection(DriverManager.java:664) at java.sql.DriverManager.getConnection(DriverManager.java:247) at sample_project.retry_0_1.Retry.tJavaFlex_1Process(Retry.java:488) at sample_project.retry_0_1.Retry.runJobInTOS(Retry.java:1431) at sample_project.retry_0_1.Retry.main(Retry.java:1269) [statistics] disconnected ジョブ Retry が 17:41 26/08/2016 に終了しました。 [終了コード=0]
三回リトライしてちゃんと失敗になりました。
最後に、ネットワーク接続が切れた状態で実行開始し、「Try count: 2」が表示されたらネットワークを接続してみます。
三回目のリトライで成功しました!
意図的にエラーを発生
「エラーとなる可能性のある処理」は何でもよいので、tJava コンポーネントなどで意図的に例外を発生させても動作確認が可能です。
ここでは tJava コンポーネントでゼロ除算をさせて例外を発生させてみました。
double dbz = 1 / 0;
期待通り三回リトライして失敗になりましたね。
まとめ
このように tJavaFlex コンポーネントひとつでリトライ処理が実現できるので、大事な処理はこのコンポーネントから繋いでおくとよいでしょう。
また、今回使った tJavaFlex コンポーネントの使い方はこれ限りというわけではありませんので、またの機会に記事の題材として取り上げられればと思います。